﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Management;
using System.Net;

namespace System.Peppers.Wmi
{
    /// <summary>
    /// Class for enumerating and modifying settings on network adapters
    /// </summary>
    public sealed class NetworkAdapterConfiguration : CimSettingID
    {
        #region Static
        private static class WmiProperties
        {
            public const string
                Index = "Index",
                MACAddress = "MACAddress",
                IPAddress = "IPAddress",
                IPEnabled = "IPEnabled",
                DefaultIPGateway = "DefaultIPGateway",
                IPSubnet = "IPSubnet",
                DatabasePath = "DatabasePath",
                DNSDomain = "DNSDomain",
                DNSServerSearchOrder = "DNSServerSearchOrder",
                DHCPLeaseExpires = "DHCPLeaseExpires",
                DHCPLeaseObtained = "DHCPLeaseObtained",
                DHCPServer = "DHCPServer",
                DHCPEnabled = "DHCPEnabled",
                TcpipNetbiosOptions = "TcpipNetbiosOptions";
        }

        private static class WmiMethods
        {
            public const string
                ReleaseDHCPLease = "ReleaseDHCPLease",
                RenewDHCPLease = "RenewDHCPLease",
                EnableDHCP = "EnableDHCP",
                EnableStatic = "EnableStatic",
                SetGateways = "SetGateways",
                SetTcpipNetbios = "SetTcpipNetbios",
                SetDatabasePath = "SetDatabasePath",
                SetTcpNumConnections = "SetTcpNumConnections";
        }

        private static class WmiParameters
        {
            public const string
                DNSServerSearchOrder = "DNSServerSearchOrder",
                IPAddress = "IPAddress",
                SubnetMask = "SubnetMask",
                DefaultIPGateway = "DefaultIPGateway",
                GatewayCostMetric = "GatewayCostMetric",
                TcpipNetbiosOptions = "TcpipNetbiosOptions",
                DatabasePath = "DatabasePath",
                TcpNumConnections = "TcpNumConnections";
        }

        private static class Util
        {
            public static string[] ToString(IPAddress[] array)
            {
                if (array == null)
                {
                    return null;
                }

                string[] ret = new string[array.Length];
                for (int i = 0; i < array.Length; i++)
                {
                    ret[i] = array[i].ToString();
                }
                return ret;
            }

            public static IPAddress[] ToIPAddress(string[] array)
            {
                if (array == null)
                {
                    return null;
                }

                IPAddress[] ret = new IPAddress[array.Length];
                for (int i = 0; i < array.Length; i++)
                {
                    ret[i] = IPAddress.Parse(array[i]);
                }
                return ret;
            }

            public static IPAddress ToIPAddress(string text)
            {
                IPAddress ret = null;
                IPAddress.TryParse(text, out ret);
                return ret;
            }
        }

        /// <summary>
        /// Enumerates adapters on the system
        /// </summary>
        /// <returns>List of adapters</returns>
        public static IEnumerable<NetworkAdapterConfiguration> GetAdapters()
        {
            foreach (ManagementObject item in GetInstances(WmiClasses.Win32_NetworkAdapterConfiguration))
            {
                yield return new NetworkAdapterConfiguration(item);
            }
        }

        /// <summary>
        /// Enumerates adapters on a remote system
        /// </summary>
        /// <param name="remoteParams">Connection options for remote system</param>
        /// <returns>List of adapters</returns>
        public static IEnumerable<NetworkAdapterConfiguration> GetAdapters(WmiRemoteParameters remoteParams)
        {
            foreach (ManagementObject item in GetInstances(WmiClasses.Win32_NetworkAdapterConfiguration, remoteParams))
            {
                yield return new NetworkAdapterConfiguration(item);
            }
        }

        /// <summary>
        /// The SetDatabasePath static method sets the path to the standard Internet database files (HOSTS, LMHOSTS, NETWORKS, and PROTOCOLS). It is used by the Windows Sockets interface. The default path is %SystemRoot%\system32\drivers\etc.
        /// </summary>
        /// <param name="path">Database path</param>
        /// <returns>Method result</returns>
        public static NetworkAdapterResult SetDatabasePath(string path)
        {
            NetworkAdapterConfiguration adapter = new NetworkAdapterConfiguration(WmiWrapper.GetStaticInstance(WmiClasses.Win32_NetworkAdapterConfiguration));
            return adapter.ExecuteNetworkMethod(WmiMethods.SetDatabasePath, delegate(ManagementBaseObject inParam)
            {
                inParam[WmiParameters.DatabasePath] = path;
            });
        }

        /// <summary>
        /// The SetDatabasePath static method sets the path to the standard Internet database files (HOSTS, LMHOSTS, NETWORKS, and PROTOCOLS) on a remote system. It is used by the Windows Sockets interface. The default path is %SystemRoot%\system32\drivers\etc.
        /// </summary>
        /// <param name="remoteParams">Connection options for remote system</param>
        /// <param name="path">Database path</param>
        /// <returns>Method result</returns>
        public static NetworkAdapterResult SetDatabasePath(WmiRemoteParameters remoteParams, string path)
        {
            using (NetworkAdapterConfiguration adapter = new NetworkAdapterConfiguration(WmiWrapper.GetStaticInstance(WmiClasses.Win32_NetworkAdapterConfiguration, remoteParams)))
            {
                return adapter.ExecuteNetworkMethod(WmiMethods.SetDatabasePath, delegate(ManagementBaseObject inParam)
                {
                    inParam[WmiParameters.DatabasePath] = path;
                });
            }
        }
        #endregion

        internal NetworkAdapterConfiguration(ManagementObject wmiObject)
            : base(wmiObject)
        { }

        /// <summary>
        /// MAC address of the adapter
        /// </summary>
        public string MacAddress
        {
            get { return Convert.ToString(this[WmiProperties.MACAddress]); }
        }

        /// <summary>
        /// Returns if adapter is enabled
        /// </summary>
        public bool IsEnabled
        {
            get { return Convert.ToBoolean(this[WmiProperties.IPEnabled] ?? false); }
        }

        /// <summary>
        /// Index of adapter
        /// </summary>
        public uint Index
        {
            get { return Convert.ToUInt32(this[WmiProperties.Index] ?? 0); }
        }

        /// <summary>
        /// Valid Windows file path to standard Internet database files (HOSTS, LMHOSTS, NETWORKS, and PROTOCOLS). The file path is used by the Windows Sockets interface. This property is only available on computer systems running Windows 2000 and Windows NT.
        /// </summary>
        public string DatabasePath
        {
            get { return Convert.ToString(this[WmiProperties.DatabasePath]); }
        }

        /// <summary>
        /// Organization name followed by a period and an extension that indicates the type of organization, such as microsoft.com. The name can be any combination of the letters A through Z, the numerals 0 through 9, and the hyphen (-), plus the period (.) character used as a separator.
        /// </summary>
        public string DnsDomain
        {
            get { return Convert.ToString(this[WmiProperties.DNSDomain]); }
        }

        /// <summary>
        /// Expiration date and time for a leased IP address that was assigned to the computer by the dynamic host configuration protocol (DHCP) server.
        /// </summary>
        public DateTime? DhcpLeaseExpires
        {
            get { return WmiUtil.ToDateTime(this[WmiProperties.DHCPLeaseExpires]); }
        }

        /// <summary>
        /// Date and time the lease was obtained for the IP address assigned to the computer by the dynamic host configuration protocol (DHCP) server.
        /// </summary>
        public DateTime? DhcpLeaseObtained
        {
            get { return WmiUtil.ToDateTime(this[WmiProperties.DHCPLeaseObtained]); }
        }

        /// <summary>
        /// DHCP server address
        /// </summary>
        public IPAddress DhcpServer
        {
            get { return Util.ToIPAddress(Convert.ToString(this[WmiProperties.DHCPServer])); }
        }

        /// <summary>
        /// Returns if DHCP is enabled
        /// </summary>
        public bool DhcpEnabled
        {
            get { return Convert.ToBoolean(this[WmiProperties.DHCPEnabled] ?? false); }
        }

        /// <summary>
        /// Returns all ip addresses on the adapter
        /// </summary>
        public IPAddress[] IPAddresses
        {
            get { return Util.ToIPAddress((string[])this[WmiProperties.IPAddress]); }
        }

        /// <summary>
        /// Returns all default gateways on the adapter
        /// </summary>
        public IPAddress[] DefaultGateways
        {
            get { return Util.ToIPAddress((string[])this[WmiProperties.DefaultIPGateway]); }
        }

        /// <summary>
        /// Returns all subnet masks on the adapter
        /// </summary>
        public IPAddress[] SubnetMasks
        {
            get { return Util.ToIPAddress((string[])this[WmiProperties.IPSubnet]); }
        }

        /// <summary>
        /// Returns all dns servers on the adapter
        /// </summary>
        public IPAddress[] DnsServers
        {
            get { return Util.ToIPAddress((string[])this[WmiProperties.DNSServerSearchOrder]); }
        }

        /// <summary>
        /// Settings related to NetBIOS over TCP/IP.
        /// </summary>
        public TcpipNetbiosOptions TcpipNetbiosOptions
        {
            get { return (TcpipNetbiosOptions)Convert.ToUInt32(this[WmiProperties.TcpipNetbiosOptions] ?? 0); }
        }

        /// <summary>
        /// Enables DHCP (disables static)
        /// </summary>
        /// <returns>Result of method</returns>
        public NetworkAdapterResult EnableDhcp()
        {
            return ExecuteNetworkMethod(WmiMethods.EnableDHCP, null);
        }

        /// <summary>
        /// Releases your DHCP-created ip address and disables the adapter
        /// </summary>
        /// <returns>Result of method</returns>
        public NetworkAdapterResult ReleaseDhcpLease()
        {
            return ExecuteNetworkMethod(WmiMethods.ReleaseDHCPLease, null);
        }

        /// <summary>
        /// The SetTcpipNetbios method is used to set the default operation of NetBIOS over TCP/IP
        /// </summary>
        /// <param name="options">Value that specifies the possible settings related to NetBIOS over TCP/IP</param>
        /// <returns>Result of method</returns>
        public NetworkAdapterResult SetNetBios(TcpipNetbiosOptions options)
        {
            return ExecuteNetworkMethod(WmiMethods.SetTcpipNetbios, delegate(ManagementBaseObject inParam)
            {
                inParam[WmiParameters.TcpipNetbiosOptions] = (uint)options;
            });
        }

        /// <summary>
        /// Requests a new ip address from the DHCP server, call after calling ReleaseDchpLease()
        /// </summary>
        /// <returns>Result of method</returns>
        public NetworkAdapterResult RenewDhcpLease()
        {
            return ExecuteNetworkMethod(WmiMethods.RenewDHCPLease, null);
        }

        /// <summary>
        /// Enables a static ip address with the supplied settings
        /// </summary>
        /// <param name="ipAddresses">IP addresses to set on adapter</param>
        /// <param name="subnetMasks">Subnet masks to use on adapter</param>
        /// <returns>Result of method</returns>
        public NetworkAdapterResult EnableStatic(IPAddress[] ipAddresses, IPAddress[] subnetMasks)
        {
            string[] ips = Util.ToString(ipAddresses),
                subnets = Util.ToString(subnetMasks);

            return ExecuteNetworkMethod(WmiMethods.EnableStatic, delegate(ManagementBaseObject inParam)
            {
                inParam[WmiParameters.IPAddress] = ips;
                inParam[WmiParameters.SubnetMask] = subnets;
            });
        }

        /// <summary>
        /// Sets DNS servers for the adapter
        /// </summary>
        /// <param name="dnsServers">DNS servers to set to the adapter</param>
        /// <returns>Result of method</returns>
        public NetworkAdapterResult SetDnsServers(IPAddress[] dnsServers)
        {
            string[] dns = Util.ToString(dnsServers);

            return ExecuteNetworkMethod(WmiMethods.EnableStatic, delegate(ManagementBaseObject inParam)
            {
                inParam[WmiParameters.DNSServerSearchOrder] = dns;
            });
        }

        /// <summary>
        /// Sets default gateways for the adapter
        /// </summary>
        /// <param name="defaultGateways">Default gateways to set to the adapter</param>
        /// <returns>Result of method</returns>
        public NetworkAdapterResult SetDefaultGateways(IPAddress[] defaultGateways)
        {
            string[] gateways = Util.ToString(defaultGateways);
            ushort[] costs = null;
            if (gateways != null)
            {
                costs = new ushort[gateways.Length];
                for (int i = 0; i < costs.Length; i++)
                {
                    costs[i] = 1;
                }
            }

            return ExecuteNetworkMethod(WmiMethods.EnableStatic, delegate(ManagementBaseObject inParam)
            {
                inParam[WmiParameters.DefaultIPGateway] = gateways;
                inParam[WmiParameters.GatewayCostMetric] = costs;
            });
        }

        /// <summary>
        /// The SetTcpNumConnections WMI class static method is used to set the maximum number of connections that TCP may have open simultaneously.
        /// </summary>
        /// <param name="tcpNumConnections">Maximum number of connections that TCP may have open simultaneously. The valid range of values is 0 - 0xFFFFFE.</param>
        /// <returns>Result of method</returns>
        public NetworkAdapterResult SetTcpNumConnections(uint tcpNumConnections)
        {
            return ExecuteNetworkMethod(WmiMethods.SetTcpNumConnections, delegate(ManagementBaseObject inParam)
            {
                inParam[WmiParameters.TcpNumConnections] = tcpNumConnections;
            });
        }

        private NetworkAdapterResult ExecuteNetworkMethod(string method, Action<ManagementBaseObject> callback)
        {
            using (ManagementBaseObject outParam = ExecuteMethod(method, callback))
            {
                return (NetworkAdapterResult)Convert.ToUInt32(outParam[WmiClasses.ReturnValue]);
            }
        }
    }
}
